import { PropertyEditor } from '../core/PropertyEditor';

/**
 * StringPropertyEditor - Property editor for string values
 */
export class StringPropertyEditor extends PropertyEditor {
  private _maxLength?: number;
  private _minLength?: number;
  private _pattern?: string;

  /**
   * Get the maximum length allowed
   */
  public get maxLength(): number | undefined {
    return this._maxLength;
  }

  /**
   * Set the maximum length allowed
   */
  public set maxLength(value: number | undefined) {
    this._maxLength = value;
    if (value) {
      this.addValidationRule(
        (val) => ({
          valid: !val || String(val).length <= value,
          error: `Maximum length is ${value} characters`
        }),
        `Maximum length is ${value} characters`
      );
    }
  }

  /**
   * Get the minimum length required
   */
  public get minLength(): number | undefined {
    return this._minLength;
  }

  /**
   * Set the minimum length required
   */
  public set minLength(value: number | undefined) {
    this._minLength = value;
    if (value) {
      this.addValidationRule(
        (val) => ({
          valid: !val || String(val).length >= value,
          error: `Minimum length is ${value} characters`
        }),
        `Minimum length is ${value} characters`
      );
    }
  }

  /**
   * Set a pattern for validation
   */
  public set pattern(value: string) {
    this._pattern = value;
    if (value) {
      this.addValidationRule(
        (val) => ({
          valid: !val || new RegExp(value).test(String(val)),
          error: `Value must match pattern ${value}`
        }),
        `Invalid format`
      );
    }
  }

  /**
   * Render this string property editor
   */
  public render(value: string, onChange: (newValue: string) => void): any {
    // Handle null or undefined value
    const safeValue = value === null || value === undefined ? '' : String(value);
    
    const editorConfig = super.render(safeValue, onChange);
    return {
      ...editorConfig,
      type: 'string',
      maxLength: this._maxLength,
      minLength: this._minLength,
      pattern: this._pattern
    };
  }
}

/**
 * NumberPropertyEditor - Property editor for numeric values
 */
export class NumberPropertyEditor extends PropertyEditor {
  private _precision: number = 2;
  private _min?: number;
  private _max?: number;

  /**
   * Get the precision
   */
  public get precision(): number {
    return this._precision;
  }

  /**
   * Set the precision
   */
  public set precision(value: number) {
    this._precision = value;
  }

  /**
   * Get the minimum value
   */
  public get min(): number | undefined {
    return this._min;
  }

  /**
   * Set the minimum value
   */
  public set min(value: number | undefined) {
    this._min = value;
    if (value !== undefined) {
      this.addValidationRule(
        (val) => ({
          valid: val === null || val === undefined || Number(val) >= value,
          error: `Minimum value is ${value}`
        }),
        `Minimum value is ${value}`
      );
    }
  }

  /**
   * Get the maximum value
   */
  public get max(): number | undefined {
    return this._max;
  }

  /**
   * Set the maximum value
   */
  public set max(value: number | undefined) {
    this._max = value;
    if (value !== undefined) {
      this.addValidationRule(
        (val) => ({
          valid: val === null || val === undefined || Number(val) <= value,
          error: `Maximum value is ${value}`
        }),
        `Maximum value is ${value}`
      );
    }
  }

  /**
   * Format a value for display
   */
  protected format(value: any): string {
    if (value === null || value === undefined || isNaN(Number(value))) {
      return '';
    }
    const num = Number(value);
    return this._precision === 0 ? Math.round(num).toString() : num.toFixed(this._precision);
  }

  /**
   * Parse a display value
   */
  protected parse(displayValue: any): number | null {
    if (displayValue === '' || displayValue === null || displayValue === undefined) {
      return null;
    }
    const num = Number(displayValue);
    if (isNaN(num)) {
      throw new Error('Invalid number format');
    }
    return this._precision === 0 ? Math.round(num) : Number(num.toFixed(this._precision));
  }

  /**
   * Render this number property editor
   */
  public render(value: number, onChange: (newValue: number) => void): any {
    // Handle null or undefined value
    const safeValue = value === null || value === undefined || isNaN(Number(value)) ? null : Number(value);
    
    const editorConfig = super.render(safeValue, onChange);
    return {
      ...editorConfig,
      type: 'number',
      min: this._min,
      max: this._max,
      step: this._precision === 0 ? 1 : Math.pow(10, -this._precision)
    };
  }
}

/**
 * BooleanPropertyEditor - Property editor for boolean values
 */
export class BooleanPropertyEditor extends PropertyEditor {
  /**
   * Format a value for display
   */
  protected format(value: any): boolean {
    return value === true;
  }

  /**
   * Parse a display value
   */
  protected parse(displayValue: any): boolean {
    return displayValue === true;
  }

  /**
   * Render this boolean property editor
   */
  public render(value: boolean, onChange: (newValue: boolean) => void): any {
    const safeValue = value === true;
    
    const editorConfig = super.render(safeValue, onChange);
    return {
      ...editorConfig,
      type: 'boolean'
    };
  }
}

/**
 * DatePropertyEditor - Property editor for date values
 */
export class DatePropertyEditor extends PropertyEditor {
  private _minDate?: Date;
  private _maxDate?: Date;

  /**
   * Get the minimum date allowed
   */
  public get minDate(): Date | undefined {
    return this._minDate;
  }

  /**
   * Set the minimum date allowed
   */
  public set minDate(value: Date | undefined) {
    this._minDate = value;
    if (value) {
      this.addValidationRule(
        (val) => ({
          valid: !val || new Date(val) >= value,
          error: `Date must be on or after ${value.toLocaleDateString()}`
        }),
        `Minimum date is ${value.toLocaleDateString()}`
      );
    }
  }

  /**
   * Get the maximum date allowed
   */
  public get maxDate(): Date | undefined {
    return this._maxDate;
  }

  /**
   * Set the maximum date allowed
   */
  public set maxDate(value: Date | undefined) {
    this._maxDate = value;
    if (value) {
      this.addValidationRule(
        (val) => ({
          valid: !val || new Date(val) <= value,
          error: `Date must be on or before ${value.toLocaleDateString()}`
        }),
        `Maximum date is ${value.toLocaleDateString()}`
      );
    }
  }

  /**
   * Format a value for display
   */
  protected format(value: any): string {
    if (!value) {
      return '';
    }
    const date = value instanceof Date ? value : new Date(value);
    return isNaN(date.getTime()) ? '' : date.toISOString().split('T')[0];
  }

  /**
   * Parse a display value
   */
  protected parse(displayValue: any): Date | null {
    if (!displayValue) {
      return null;
    }
    const date = new Date(displayValue);
    if (isNaN(date.getTime())) {
      throw new Error('Invalid date format');
    }
    return date;
  }

  /**
   * Render this date property editor
   */
  public render(value: string | Date, onChange: (newValue: Date) => void): any {
    const editorConfig = super.render(value, onChange);
    return {
      ...editorConfig,
      type: 'date',
      min: this._minDate ? this.format(this._minDate) : undefined,
      max: this._maxDate ? this.format(this._maxDate) : undefined
    };
  }
}

/**
 * DropDownPropertyEditor - Property editor for selecting from a list of options
 */
export class DropDownPropertyEditor extends PropertyEditor {
  private _items: Array<{ value: any; text: string }> = [];

  /**
   * Set the list of items for the dropdown
   */
  public setItems(items: Array<{ value: any; text: string }>): void {
    this._items = items;
    this.addValidationRule(
      (val) => ({
        valid: !val || this._items.some(item => item.value === val),
        error: 'Invalid selection'
      }),
      'Please select a valid option'
    );
  }

  /**
   * Get the list of items
   */
  public getItems(): Array<{ value: any; text: string }> {
    return [...this._items];
  }

  /**
   * Format a value for display
   */
  protected format(value: any): any {
    return value;
  }

  /**
   * Parse a display value
   */
  protected parse(displayValue: any): any {
    if (!displayValue && displayValue !== 0 && displayValue !== false) {
      return null;
    }
    return displayValue;
  }

  /**
   * Render this dropdown property editor
   */
  public render(value: any, onChange: (newValue: any) => void): any {
    const editorConfig = super.render(value, onChange);
    return {
      ...editorConfig,
      type: 'dropdown',
      items: this._items
    };
  }
}
